package com.kmxd.ams.app.system;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNode;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Maps;
import com.kmxd.ams.client.system.IConfI18nDetailSvc;
import com.kmxd.ams.client.system.IConfI18nSvc;
import com.kmxd.ams.client.system.dto.*;
import com.kmxd.ams.client.system.vo.ConfI18nDetailListVO;
import com.kmxd.ams.client.system.vo.ConfI18nListVO;
import com.kmxd.ams.client.system.vo.ConfI18nUserListVO;
import com.kmxd.ams.client.system.vo.ConfI18nVO;
import com.kmxd.ams.core.common.constant.CacheConst;
import com.kmxd.ams.core.common.enums.I18nTypeEnum;
import com.kmxd.ams.core.util.IdUtil;
import com.kmxd.ams.core.util.SecurityUtil;
import com.kmxd.ams.infra.system.entity.ConfI18n;
import com.kmxd.ams.infra.system.entity.ConfI18nDetail;
import com.kmxd.ams.infra.system.mapper.ConfI18nMapper;
import java.util.*;
import java.util.stream.Collectors;
import lombok.AllArgsConstructor;
import org.apache.commons.collections4.map.HashedMap;
import org.apache.commons.compress.utils.Lists;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

/**
 * 国际化语言配置 服务实现类
 *
 * @author ZuoLG
 * @since 2022-07-14
 */
@Service
@AllArgsConstructor
@CacheConfig(cacheNames = CacheConst.CACHE_PREFIX_CONF_I18N)
public class ConfI18nSvcImpl extends ServiceImpl<ConfI18nMapper, ConfI18n> implements IConfI18nSvc {

  private ConfI18nMapper confI18nMapper;
  private IConfI18nDetailSvc confI18nDetailSvc;

  @Cacheable(keyGenerator = "myKeyGenerator")
  @Override
  public Page<ConfI18nListVO> selectPage(Page reqPage, ConfI18nQueryDTO req) {
    LambdaQueryWrapper<ConfI18n> queryWrapper =
        Wrappers.<ConfI18n>lambdaQuery()
            .eq(Objects.nonNull(req.getPid()), ConfI18n::getPid, req.getPid())
            .eq(Objects.nonNull(req.getType()), ConfI18n::getType, req.getType())
            .orderByDesc(ConfI18n::getUpdateTime);
    Page<ConfI18n> page = confI18nMapper.selectPage(reqPage, queryWrapper);
    Page<ConfI18nListVO> voPage = BeanUtil.copyProperties(page, Page.class);
    voPage.setRecords(BeanUtil.copyToList(page.getRecords(), ConfI18nListVO.class));
    return voPage;
  }

  @CacheEvict(allEntries = true)
  @Override
  @Transactional(rollbackFor = Exception.class)
  public void add(ConfI18nAddDTO req) {
    ConfI18n entity = BeanUtil.copyProperties(req, ConfI18n.class);
    if (null == entity.getPid()) {
      entity.setPid(0L);
    }
    entity.setCode(entity.getCode().toLowerCase());
    confI18nMapper.insert(entity);
  }

  @CacheEvict(allEntries = true)
  @Override
  @Transactional(rollbackFor = Exception.class)
  public void edit(ConfI18nEditDTO req) {
    ConfI18n entity = BeanUtil.copyProperties(req, ConfI18n.class);
    if (null == entity.getPid()) {
      entity.setPid(0L);
    }
    entity.setCode(entity.getCode().toLowerCase());
    confI18nMapper.updateById(entity);
  }

  @CacheEvict(allEntries = true)
  @Transactional(rollbackFor = Exception.class)
  @Override
  public void remove(String ids) {
    Set<Long> idList = IdUtil.split(ids);
    confI18nMapper.deleteBatchIds(idList);
  }

  @Cacheable(keyGenerator = "myKeyGenerator")
  @Override
  public ConfI18nVO view(Long id) {
    ConfI18n entity = confI18nMapper.selectById(id);
    return BeanUtil.copyProperties(entity, ConfI18nVO.class);
  }

  @Cacheable(keyGenerator = "myKeyGenerator")
  @Override
  public List<Tree<String>> tree() {
    List<ConfI18n> confI18ns = confI18nMapper.selectList(Wrappers.emptyWrapper());
    List treeNodeList =
        confI18ns.stream()
            .map(
                item -> {
                  TreeNode treeNode = new TreeNode();
                  treeNode.setId(item.getId().toString());
                  treeNode.setName(
                      item.getCode()
                          + "("
                          + Optional.ofNullable(item.getRemarks()).orElse("")
                          + ")");
                  treeNode.setParentId(item.getPid().toString());
                  treeNode.setWeight(item.getSort());
                  return treeNode;
                })
            .collect(Collectors.toList());
    List<Tree<String>> treeList = TreeUtil.build(treeNodeList, "0");
    return CollectionUtils.isEmpty(treeList) ? new ArrayList() : treeList;
  }

  @Override
  public String format(String code, I18nTypeEnum type, Object... content) {
    ConfI18n one = getByCodeAndType(code, type);
    if (Objects.isNull(one)) {
      return "";
    }

    ConfI18nDetail detail = getDetailByConfIdAndLangCode(one);
    if (Objects.isNull(detail)) {
      return "";
    }
    if (ObjectUtil.isEmpty(content)) {
      return detail.getLangContent();
    } else {
      return StrUtil.format(detail.getLangContent(), content);
    }
  }

  @Cacheable(keyGenerator = "myKeyGenerator")
  @Override
  public ConfI18nDetail getDetailByConfIdAndLangCode(ConfI18n one) {
    return confI18nDetailSvc.getOne(
        Wrappers.<ConfI18nDetail>lambdaQuery()
            .eq(ConfI18nDetail::getConfId, one.getId())
            .eq(ConfI18nDetail::getLangCode, SecurityUtil.getLangCode()));
  }

  @Cacheable(keyGenerator = "myKeyGenerator")
  @Override
  public ConfI18n getByCodeAndType(String code, I18nTypeEnum type) {
    return getOne(
        Wrappers.<ConfI18n>lambdaQuery()
            .eq(ConfI18n::getCode, code.toLowerCase())
            .eq(ConfI18n::getType, type));
  }

  @Override
  public String format(String code, I18nTypeEnum type) {
    return format(code, type, (Object) null);
  }

  @Override
  public String format(String code) {
    return format(code, I18nTypeEnum.PROMPT_MESSAGE, (Object) null);
  }

  @Override
  public String format(ConfI18nFormatQueryDTO req) {
    return format(req.getCode(), req.getType(), (Object) null);
  }

  @Override
  public List<ConfI18nUserListVO> getI18nList(String langCode) {
    List<ConfI18n> list = list();
    if (ObjectUtil.isEmpty(list)) {
      return Lists.newArrayList();
    }
    ConfI18nDetailQueryDTO qryDetail = new ConfI18nDetailQueryDTO();
    qryDetail.setLangCode(langCode);
    List<ConfI18nDetailListVO> detailList = confI18nDetailSvc.selectList(qryDetail);
    if (ObjectUtil.isEmpty(detailList)) {
      return Lists.newArrayList();
    }
    Map<Long, List<ConfI18nDetailListVO>> group =
        detailList.stream().collect(Collectors.groupingBy(ConfI18nDetailListVO::getConfId));
    return list.stream()
        .map(
            item -> {
              List<ConfI18nDetailListVO> details = group.get(item.getId());
              if (ObjectUtil.isEmpty(details)) {
                return Lists.<ConfI18nUserListVO>newArrayList();
              }
              return details.stream()
                  .map(
                      detail -> {
                        ConfI18nUserListVO vo = new ConfI18nUserListVO();
                        vo.setCode(item.getCode());
                        vo.setValue(detail.getLangContent());
                        vo.setType(item.getType());
                        return vo;
                      })
                  .collect(Collectors.toList());
            })
        .flatMap(Collection::stream)
        .collect(Collectors.toList());
  }

  @Override
  public Map<String, Map<String, String>> getUserI18nList(String langCode) {
    List<ConfI18nUserListVO> i18nList = getI18nList(langCode);
    if (ObjectUtil.isEmpty(i18nList)) {
      return Maps.newHashMap();
    }
    Map<String, Map<String, String>> res = new HashedMap<>();
    i18nList.stream()
        .collect(Collectors.groupingBy(ConfI18nUserListVO::getType))
        .forEach(
            (key, value) -> {
              if (ObjectUtil.isEmpty(value)) {
                res.put(String.valueOf(key), Maps.newHashMap());
                return;
              }
              Map<String, String> collect =
                  value.stream()
                      .map(
                          detail -> {
                            Map<String, String> map = new HashedMap<>();
                            map.put(detail.getCode(), detail.getValue());
                            return map;
                          })
                      .flatMap(e -> e.entrySet().stream())
                      .collect(
                          HashMap::new, (m, v) -> m.put(v.getKey(), v.getValue()), HashMap::putAll);
              res.put(String.valueOf(key), collect);
            });
    return res;
  }
}
